Tổng quan: Ở bài tập này, chúng ta sẽ thực hành lập trình một số hàm trích xuất đặc trưng ảnh cơ bản, sau đó ứng dụng cho các bài toán tạo ảnh panorama và bài toán đối sánh để tìm kiếm ảnh.
Yêu cầu thư viện: OpenCV 3.3, matplotlib, skimage
from IPython.display import Image
from skimage.feature import hog
import skimage
import matplotlib.pyplot as plt
import os
import cv2
import numpy as np
Harris Corner là một phương pháp phát hiện các điểm (có tính chất) góc trong ảnh, thường được sử dụng khi tính toán các đặc trưng ảnh cho các bài toán thị giác máy tính.
Thư viện OpenCV cung cấp hàm cv2.cornerHarris() để phát hiện các điểm góc trong ảnh. Tham số như sau:
Hãy hoàn thành hàm detect_corner(), với các tham số sau:
Input:
Output:
def detect_corner(image_path, blockSize=2, ksize=3, k=0.04, threshold=0.01):
img = cv2.imread(image_path)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,blockSize,ksize,k)
#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>threshold*dst.max()]=[0,0,255]
cv2.imwrite('corner_' + image_path,img)
return 'corner_' + image_path
out_path = detect_corner('chessboard.jpg')
# out_path = detect_corner('sudoku.png')
Image(out_path)
Hãy thử tùy chỉnh tham số của hàm detect_corner() với ảnh 'house.jpg' sau sao cho ảnh đầu ra phát hiện được nhiều góc chính xác nhất.
Image('sudoku.png')
### YOUR CODE HERE ###
out_path = None
pass
### YOUR CODE HERE ###
Image(out_path)
Image('house.jpg')
### YOUR CODE HERE ###
out_path = None
pass
### YOUR CODE HERE ###
Image(out_path)
Histogram of Oriented Gradients (HOG) là bộ mô tả đặc trưng thường được sử dụng trong thị giác máy tính và xử lí ảnh để biểu diễn đối tượng trong ảnh
Thư viện scikit-image (skimage) cung cấp hàm skimage.feature.hog() để trích chọn đặc trưng HOG. Một số tham số quan trọng của hàm như sau:
Xem đầy đủ tại: http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.hog
Tutorial chi tiết về HOG: https://www.learnopencv.com/histogram-of-oriented-gradients/
image = cv2.imread('man.png')
fd, hog_image = hog(image, orientations=8, pixels_per_cell=(16, 16),
cells_per_block=(1, 1), visualize=True, multichannel=True)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True)
ax1.axis('off')
ax1.imshow(image, cmap=plt.cm.gray)
ax1.set_title('Input image')
# Rescale histogram for better display
hog_image_rescaled = skimage.exposure.rescale_intensity(hog_image, in_range=(0, 10))
ax2.axis('off')
ax2.imshow(hog_image_rescaled, cmap=plt.cm.gray)
ax2.set_title('Histogram of Oriented Gradients')
plt.show()
Thư viện OpenCV cung cấp các hàm liên quan đến trích chọn đặc trưng SIFT.
https://docs.opencv.org/3.4/d5/d3c/classcv_1_1xfeatures2d_1_1SIFT.html
Để khởi tạo đối tượng SIFT trong OpenCV ta sử dụng lệnh: sift = cv2.xfeatures2d.SIFT_create().
Đối tượng này có phương thức detectAndCompute trả về 2 outputs kp và des, kp là một list chứa các keypoints được detect bởi SIFT, des là một numpy array chứa len(kp) vectors 128 chiều.
Chúng ta sẽ dùng các des này để phục vụ bài toán phân loại.
img = cv2.imread('ville01002.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp, des = sift.detectAndCompute(gray,None)
img=cv2.drawKeypoints(gray,kp,img)
cv2.imwrite('sift_keypoints.jpg',img)
Image('sift_keypoints.jpg')
Speeded-Up Robust Features (SURF)
img = cv2.imread('box.png', 0)
minHessian = 400
detector = cv2.xfeatures2d_SURF.create(hessianThreshold=minHessian)
keypoints = detector.detect(img)
#-- Draw keypoints
img_keypoints = np.empty((img.shape[0], img.shape[1], 3), dtype=np.uint8)
cv2.drawKeypoints(img, keypoints, img_keypoints)
cv2.imwrite('surf_keypoints.jpg',img_keypoints)
Image('surf_keypoints.jpg')
imageA = cv2.imread('./panorama/mountain1_left.png')
imageB = cv2.imread('./panorama/mountain1_right.png')
stitcher = cv2.createStitcher(False)
result = stitcher.stitch((imageA, imageB))
cv2.imwrite('mountain1.png', result[1])
Image('mountain1.png')
imageA = cv2.imread('./panorama/scottsdale_left.png')
imageB = cv2.imread('./panorama/scottsdale_right.png')
stitcher = cv2.createStitcher(False)
result = stitcher.stitch((imageA, imageB))
cv2.imwrite('scottsdale.png', result[1])
Image('scottsdale.png')
def detectAndDescribe(image):
# convert the image to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect and extract features from the image
descriptor = cv2.xfeatures2d.SIFT_create()
(kps, features) = descriptor.detectAndCompute(image, None)
# convert the keypoints from KeyPoint objects to NumPy arrays
kps = np.float32([kp.pt for kp in kps])
# return a tuple of keypoints and features
return (kps, features)
def matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
# compute the raw matches and initialize the list of actual
# matches
matcher = cv2.DescriptorMatcher_create("BruteForce")
rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
matches = []
# loop over the raw matches
for m in rawMatches:
# ensure the distance is within a certain ratio of each
# other (i.e. Lowe's ratio test)
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
matches.append((m[0].trainIdx, m[0].queryIdx))
# computing a homography requires at least 4 matches
if len(matches) > 4:
# construct the two sets of points
ptsA = np.float32([kpsA[i] for (_, i) in matches])
ptsB = np.float32([kpsB[i] for (i, _) in matches])
# compute the homography between the two sets of points
(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,
reprojThresh)
# return the matches along with the homograpy matrix
# and status of each matched point
return (matches, H, status)
# otherwise, no homograpy could be computed
return None
def drawMatches(imageA, imageB, kpsA, kpsB, matches, status):
# initialize the output visualization image
(hA, wA) = imageA.shape[:2]
(hB, wB) = imageB.shape[:2]
vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
vis[0:hA, 0:wA] = imageA
vis[0:hB, wA:] = imageB
# loop over the matches
for ((trainIdx, queryIdx), s) in zip(matches, status):
# only process the match if the keypoint was successfully
# matched
if s == 1:
# draw the match
ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
cv2.line(vis, ptA, ptB, (0, 255, 0), 1)
# return the visualization
return vis
def stitch(images, ratio=0.75, reprojThresh=4.0, showMatches=False):
# unpack the images, then detect keypoints and extract
# local invariant descriptors from them
(imageB, imageA) = images
(kpsA, featuresA) = detectAndDescribe(imageA)
(kpsB, featuresB) = detectAndDescribe(imageB)
# match features between the two images
M = matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)
# if the match is None, then there aren't enough matched
# keypoints to create a panorama
if M is None:
return None
# otherwise, apply a perspective warp to stitch the images
# together
(matches, H, status) = M
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
# check to see if the keypoint matches should be visualized
if showMatches:
vis = drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
# return a tuple of the stitched image and the
# visualization
return (result, vis)
# return the stitched image
return result
imageA = cv2.imread('./panorama/mountain1_left.png')
imageB = cv2.imread('./panorama/mountain1_right.png')
# stitch the images together to create a panorama
(result, vis) = stitch([imageA, imageB], showMatches=True)
cv2.imwrite('matching.png', vis)
Image('matching.png')
cv2.imwrite('result.png', result)
Image('result.png')
img1 = cv2.imread('thaprua.jpg')
img2 = cv2.imread('thaprua2.jpg')
img3 = cv2.imread('chuamotcot.jpg')
img4 = cv2.imread('cotco.jpg')
img1_ = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
img2_ = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
img3_ = cv2.cvtColor(img3, cv2.COLOR_BGR2RGB)
img4_ = cv2.cvtColor(img4, cv2.COLOR_BGR2RGB)
plt.subplot(231), plt.imshow(img1_)
plt.subplot(234), plt.imshow(img2_)
plt.subplot(235), plt.imshow(img3_)
plt.subplot(236), plt.imshow(img4_)
sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
kp3, des3 = sift.detectAndCompute(img3,None)
kp4, des4 = sift.detectAndCompute(img4,None)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches2 = flann.knnMatch(des1,des2,k=2)
matches3 = flann.knnMatch(des1,des3,k=2)
matches4 = flann.knnMatch(des1,des4,k=2)
cnt = 0
matchesMask = [[0,0] for i in range(len(matches2))]
for i,(m,n) in enumerate(matches2):
if m.distance < 0.7*n.distance:
matchesMask[i]=[1,0]
cnt += 1
print("Number of matchesMask: " + str(cnt))
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = matchesMask,
flags = 0)
imgRes = cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches2,None,**draw_params)
plt.imshow(imgRes,),plt.show()
cnt = 0
matchesMask = [[0,0] for i in range(len(matches3))]
for i,(m,n) in enumerate(matches3):
if m.distance < 0.7*n.distance:
matchesMask[i]=[1,0]
cnt += 1
print("Number of matchesMask: " + str(cnt))
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = matchesMask,
flags = 0)
imgRes = cv2.drawMatchesKnn(img1,kp1,img3,kp3,matches3,None,**draw_params)
plt.imshow(imgRes,),plt.show()
cnt = 0
matchesMask = [[0,0] for i in range(len(matches4))]
for i,(m,n) in enumerate(matches4):
if m.distance < 0.7*n.distance:
matchesMask[i]=[1,0]
cnt += 1
print("Number of matchesMask: " + str(cnt))
draw_params = dict(matchColor = (0,255,0),
singlePointColor = (255,0,0),
matchesMask = matchesMask,
flags = 0)
imgRes = cv2.drawMatchesKnn(img1,kp1,img4,kp4,matches4,None,**draw_params)
plt.imshow(imgRes,),plt.show()